08. Modifying a path
Kotlin SM A08 Modifying A Path
Modifying a path
In this step you will build an animation that follows a complex path during the animation and animates the credits during the motion. MotionLayout can modify the path that a view will take between the start and the end using a KeyPosition.
After you complete this step, you'll have implemented the following animation driven by clicking on the moon.
Kotlin SM A09 Step 3 Activity
Explore the existing code
Open
layout/activity_step3.xmlandxml/step3.xmlto see the existing layout and motion scene.An
ImageViewandTextViewdisplay the moon and credits text.
Open the motion scene file (xml/step3.xml).
You see that aTransitionfrom@id/startto@id/endis defined. The animation moves the moon image from the lower left of the screen to the bottom right of the screen using twoConstraintSets. The credits text fades in fromalpha="0.0"toalpha="1.0"as the moon is moving.Run the app now and select Step 3. You'll see that the moon follows a linear path (or a straight line) from start to end when you click on the moon.
Enabling path debugging
Before you add an arc to the moon's motion, it's helpful to enable path debugging in MotionLayout.
To help develop complex animations with MotionLayout, you can draw the animation path of every view. This is helpful when you want to visualize your animation at a glance, and for fine tuning the small details of motion.
- To enable debugging paths, open
layout/activity_step3.xmland addapp:motionDebug="SHOW_PATH"to theMotionLayouttag.
<!-- layout/activity_step3.xml -->
<!-- Add app:motionDebug="SHOW_PATH" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
app:motionDebug="SHOW_PATH" >
After you enable path debugging, when you run the app again you'll see the paths of all views visualized with a dotted line.
- Circles - represent the start or end position of one view
- Lines - represent the path of one view
- Diamonds - represent a KeyPosition that modifies the path
For example, in this animation, the middle circle is the position of the credits text.
Modifying a path
All animations in MotionLayout are defined by a start and end ConstraintSet that define what the screen looks like before the animation starts and after the animation is done. By default, MotionLayout will plot a linear path (or a straight line) between the start and end position of each view that changes position.
To build complex paths like the arc of the moon in this example, MotionLayout uses a KeyPosition to modify the path that a view takes between the start and end.
A KeyPosition modifies the path a view takes between the start and the end ConstraintSet.
It can distort the path of a view to go through a third (or fourth, or fifth, …) point between the start and end positions. Or, it can even speed up and slow down progress along either the X or Y axis.
**A KeyPosition can only change the path during the animation, it cannot change the start or the end. **
The ConstraintSet will always specify the final position for the views at the start and end of the animation.
- Open
xml/step3.xmland add aKeyPositionto the scene.
<!-- xml/step3.xml -->
<!-- TODO: Add KeyFrameSet and KeyPosition -->
<KeyFrameSet>
<KeyPosition
app:framePosition="50"
app:motionTarget="@id/moon"
app:keyPositionType="parentRelative"
app:percentY="0.5"
/>
</KeyFrameSet>
A KeyFrameSet is a child of a Transition, and it is a set of all the KeyFrames, such as KeyPosition, that should be applied during the transition.
As MotionLayout is calculating the path for the moon between the start and the end, it modifies the path based on the KeyPosition specified in the KeyFrameSet. You can see how this modifies the path by running the app again.
A KeyPosition has several attributes that describe how it modifies the path, the most important ones are:
framePosition- a number between 0 and 100 to say when in the animation this KeyPosition should be applied, with 1 being 1% through the animation, and 99 being 99% through the animation. So if the value is 50, we apply it right in the middle.motionTarget- the view for which this KeyPosition modifies the pathkeyPositionType- how this KeyPosition modifies the path. It can be either parentRelative, pathRelative, or deltaRelative (explained in the next step).percentX|percentY- how much to modify the path at framePosition (values between 0.0 and 1.0, with negative values and values >1 allowed)
You can think of it this way, "At framePosition modify the path of motionTarget by moving it by percentX or percentY` according to the coordinates determined by keyPositionType."
By default MotionLayout will round any corners that are introduced by modifying the path. If you look at the animation you just created, you can see the moon follows a curved path at the bend. For most animations, this is what you want, and if not you can specify the curveFit attribute to customize it.
Try it out
If you run the app again, you'll see the animation for this step.
The moon follows an arc because it goes through a KeyPosition specified in the Transition.
<KeyPosition
app:framePosition="50"
app:motionTarget="@id/moon"
app:keyPositionType="parentRelative"
app:percentY="0.5"
/>
You can read this KeyPosition as, "At framePosition 50 (halfway through the animation) modify the path of motionTarget @id/moon by moving it by 50% Y (halfway down the screen) according to the coordinates determined by parentRelative (the entire MotionLayout)."
So, half way through the animation, the moon must go through a KeyPosition that's 50% down on the screen. This KeyPosition doesn't modify the X motion at all, so the moon will still go from start to end horizontally. MotionLayout will figure out a smooth path that goes through this KeyPosition and while moving between start and end.
If you look closely, the credits text is constrained by the position of the moon. Why isn't it moving vertically as well?
<Constraint
android:id="@id/credits"
...
app:layout_constraintBottom_toBottomOf="@id/moon"
app:layout_constraintTop_toTopOf="@id/moon"
/>
It turns out, even though we're modifying the path that the moon takes, the start and end positions of the moon don't move it vertically at all. The KeyPosition doesn't modify the start or the end position, so the credits text is constrained to the final end position of the moon.
If you did want the credits to move with the moon, you could add a KeyPosition to the credits, or modify the start constraints on @id/credits.
Start and end constraints are never modified by a KeyPosition.
Even if a view is following a complex motion path, like @id/moon, the start and end constraints will always rest at their initial or final positions. Other views constrained to @id/moon will not follow the path in between.
In the next section we'll dive into the different types of keyPositionType in MotionLayout.